﻿using System;

namespace Microsoft.Xna.Framework.Content
{
	/// <summary>
	/// Worker for reading a specific managed type from a binary format.
	/// </summary>
    public abstract class ContentTypeReader
    {
        #region Private Member Variables
		internal readonly bool TargetIsValueType;
        private Type targetType;
        #endregion
    
        #region Properties
		/// <summary>
		/// Determines if deserialization into an existing object is possible.
		/// </summary>
		public virtual bool CanDeserializeIntoExistingObject
		{
			get { return false; }
		}
 
		/// <summary>
		/// Gets the type handled by this reader component.
		/// </summary>
        public Type TargetType
        {
            get { return this.targetType; }
        }

		/// <summary>
		/// Gets a format version number for this type.
		/// </summary>
        public virtual int TypeVersion
        {
            get { return 0; }   // The default version (unless overridden) is zero
        }
        #endregion

        #region Constructors
		/// <summary>
		/// Creates a new instance of ContentTypeReader.
		/// </summary>
		/// <param name="targetType">The type handled by this reader component.</param>
        protected ContentTypeReader(Type targetType)
        {
			this.targetType = targetType;
			if (targetType != null)
			{
				this.TargetIsValueType = targetType.IsValueType;
			}
        }
        #endregion

        #region Protected Methods
		/// <summary>
		/// Retrieves and caches nested type readers. Called by the framework at creation time.
		/// </summary>
		/// <param name="manager">The content manager.</param>
        protected internal virtual void Initialize(ContentTypeReaderManager manager)
        {
            // Do nothing. Are we supposed to add ourselves to the manager?
        }

		/// <summary>
		/// Reads a strongly typed object from the current stream.
		/// </summary>
		/// <param name="input">The ContentReader used to read the object.</param>
		/// <param name="existingInstance">The object receiving the data, or null if a new instance of the object should be created.</param>
        protected internal abstract object Read(ContentReader input, object existingInstance);
        #endregion
    }

	/// <summary>
	/// Worker for reading a specific managed type from a binary format. Derive from this class to add new data types to the content pipeline system.
	/// </summary>
	/// <typeparam name="T"></typeparam>
    public abstract class ContentTypeReader<T> : ContentTypeReader
    {
        #region Constructors
		/// <summary>
		/// Creates a new instance of ContentTypeReader.
		/// </summary>
        protected ContentTypeReader()
            : base(typeof(T))
        {
            // Nothing
        }
        #endregion

        #region Protected Methods
		/// <summary>
		/// Reads an object from the current stream.
		/// </summary>
		/// <param name="input">The ContentReader used to read the object.</param>
		/// <param name="existingInstance">An existing object to read into.</param>
        protected internal override object Read(ContentReader input, object existingInstance)
        {
			T local;
			if (existingInstance == null)
			{
				local = default(T);
			}
			else
			{
				if (!(existingInstance is T))
				{
					throw input.CreateContentLoadException(FrameworkResources.BadXnbWrongType, typeof(T), existingInstance.GetType());
				}
				local = (T)existingInstance;
			}
			return this.Read(input, local);
        }

		/// <summary>
		/// Reads a strongly typed object from the current stream.
		/// </summary>
		/// <param name="input">The ContentReader used to read the object.</param>
		/// <param name="existingInstance">An existing object to read into.</param>
        protected internal abstract T Read(ContentReader input, T existingInstance);
        #endregion
    }
}
